﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Mahanet.Abyss.PathCalculation
{
	public class Vector3D
	{
		#region C'tor

		public Vector3D(double x, double y, double z)
		{
			this.X = x;
			this.Y = y;
			this.Z = z;
		}

		public Vector3D(Coordinate3D from, Coordinate3D to)
		{
			this.X = to.X - from.X;
			this.Y = to.Y - from.Y;
			this.Z = to.Z - from.Z;
		}

		#endregion

		#region Public Properties

		public double X { get; private set; }
		public double Y { get; private set; }
		public double Z { get; private set; }

		#endregion

		#region Public Methods

		public double Dot(Vector3D other)
		{
			return X * other.X + Y * other.Y + Z * other.Z;
		}

		public Vector3D Cross(Vector3D other)
		{
			return new Vector3D(Y * other.Z - Z * other.Y,
								Z * other.X - X * other.Z,
								X * other.Y - Y * other.X);
		}

		public double LengthSquared()
		{
			return X * X + Y * Y + Z * Z;
		}

		public double Length()
		{
			return Math.Sqrt(LengthSquared());
		}

		public Vector3D Normalized()
		{
			double length = Length();
			if (length == 0.0)
			{
				return this.MemberwiseClone() as Vector3D;
			}

			return this / length;
		}

		public double AngleTo(Vector3D other)
		{
			return Math.Acos(this.Dot(other) / (this.Length() * other.Length()));
		}

		public static Vector3D operator *(Vector3D vector, double scalar)
		{
			return scalar * vector;
		}

		public static Vector3D operator *(double scalar, Vector3D vector)
		{
			return new Vector3D(vector.X * scalar, vector.Y * scalar, vector.Z * scalar);
		}

		public static Vector3D operator /(Vector3D vector, double scalar)
		{
			return new Vector3D(vector.X / scalar, vector.Y / scalar, vector.Z / scalar);
		}

		public static Vector3D operator +(Vector3D a, Vector3D b)
		{
			return new Vector3D(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
		}

		public static Coordinate3D operator +(Vector3D vector, Coordinate3D coordinate)
		{
			return coordinate + vector;
		}

		public static Coordinate3D operator +(Coordinate3D coordinate, Vector3D vector)
		{
			return new Coordinate3D(vector.X + coordinate.X, vector.Y + coordinate.Y, vector.Z + coordinate.Z); 
		}

		public static Coordinate3D operator -(Vector3D a, Vector3D b)
		{
			return new Coordinate3D(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
		}

		public static Vector3D operator -(Vector3D vector)
		{
			return new Vector3D(-vector.X, -vector.Y, -vector.Z);
		}

		public bool IsZero()
		{
			return X == 0 && Y == 0 && Z == 0;
		}

		#endregion
	}
}
